// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第二，第三，第二，第二，第二，第二，第二，第二，第二，第二，第二，第三，第三，第三，第C.所以“

package runtime

import (
	"unsafe"
)


// go:linkname libc_rctlblk_size libc_rctlblk_size 

var (
	libc_getrctl,
	libc_rctlblk_get_local_action,
	libc_rctlblk_get_local_flags,
	libc_rctlblk_get_value,
	libc_rctlblk_size libcFunc
)

// 返回区域CPU上限的最小值，如果未检测到上限，则返回0。
func getcpucap() uint64 {
	// 资源控制块是一个不透明对象，其大小仅为
	// libc已知。实际上，考虑到内容，
	// 不太可能超过8KB，因此我们将在这里使用该大小的静态缓冲区。
	const rblkmaxsize = 8 * 1024
	if rctlblk_size() > rblkmaxsize {
		return 0
	}

	// “zone.cpu cap”资源控制，如
	// resource_controls（5）中所述，“设置一个区域可以使用
	// 的cpu时间量的限制。使用的单位是一个区域中所有用户线程可以使用的单个
	// cpu的百分比，用
	// 整数表示。”名称的C字符串必须传递给getrctl（2）。
	name := []byte("zone.cpu-cap\x00")

	// 要遍历特定资源的值列表
	// 控件，我们需要两个块：一个用于之前读取的值，另一个用于下一个值。
	var rblk0 [rblkmaxsize]byte
	var rblk1 [rblkmaxsize]byte
	rblk := &rblk0[0]
	rblkprev := &rblk1[0]

	var flag uint32 = _RCTL_FIRST
	var capval uint64 = 0

	for {
		if getrctl(unsafe.Pointer(&name[0]), unsafe.Pointer(rblkprev), unsafe.Pointer(rblk), flag) != 0 {
			// 序列的结尾被报告为ENONT 
			// 故障，但确定CPU上限在这里并不重要。我们会把任何失败当作序列的结束来对待。
			break
		}

		lflags := rctlblk_get_local_flags(unsafe.Pointer(rblk))
		action := rctlblk_get_local_action(unsafe.Pointer(rblk))
		if (lflags&_RCTL_LOCAL_MAXIMAL) == 0 && action == _RCTL_LOCAL_DENY {
			// 这是一个有限（非最大）值，表示
			// 上限（拒绝）操作。
			v := rctlblk_get_value(unsafe.Pointer(rblk))
			if capval == 0 || capval > v {
				capval = v
			}
		}

		// 交换数据块，这样我们就可以获取下一个值
		t := rblk
		rblk = rblkprev
		rblkprev = t
		flag = _RCTL_NEXT
	}

	return capval
}

func getncpu() int32 {
	n := int32(sysconf(__SC_NPROCESSORS_ONLN))
	if n < 1 {
		return 1
	}

	if cents := int32(getcpucap()); cents > 0 {
		// 将CPU的百分比转换为多个CPU，
		// 四舍五入以使用分数CPU 
		// 例如。，336%变成4个CPU 
		ncap := (cents + 99) / 100
		if ncap < n {
			return ncap
		}
	}

	return n
}

// go:nosplit 
func getrctl(controlname, oldbuf, newbuf unsafe.Pointer, flags uint32) uintptr {
	return sysvicall4(&libc_getrctl, uintptr(controlname), uintptr(oldbuf), uintptr(newbuf), uintptr(flags))
}

// go:nosplit 
func rctlblk_get_local_action(buf unsafe.Pointer) uintptr {
	return sysvicall2(&libc_rctlblk_get_local_action, uintptr(buf), uintptr(0))
}

// go:nosplit 
func rctlblk_get_local_flags(buf unsafe.Pointer) uintptr {
	return sysvicall1(&libc_rctlblk_get_local_flags, uintptr(buf))
}

// go:nosplit 
func rctlblk_get_value(buf unsafe.Pointer) uint64 {
	return uint64(sysvicall1(&libc_rctlblk_get_value, uintptr(buf)))
}

// go nosplit 
func rctlblk_size() uintptr {
	return sysvicall0(&libc_rctlblk_size)
}
